package bocchi.general;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ClassUtil {

    /**
     * 获取指定包下所有继承自 parentClass 的子类
     *
     * @param packageName 包名
     * @param parentClass 父类
     * @return 子类列表
     * @throws ClassNotFoundException 如果类加载失败
     */
    public static List<Class<?>> getAllSubclasses(String packageName, Class<?> parentClass) throws ClassNotFoundException {
        List<Class<?>> subclasses = new ArrayList<>();
        List<Class<?>> allClasses = getClassesFromJar(packageName);

        for (Class<?> clazz : allClasses) {
            if (parentClass.isAssignableFrom(clazz) && !parentClass.equals(clazz)) {
                subclasses.add(clazz);
            }
        }

        return subclasses;
    }

    private static List<Class<?>> getClassesFromJar(String packageName) throws ClassNotFoundException {
        List<Class<?>> classes = new ArrayList<>();
        String packagePath = packageName.replace('.', '/');
        ClassLoader classLoader = ClassUtil.class.getClassLoader();

        try {
            Enumeration<URL> resources = classLoader.getResources(packagePath);
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                if (!resource.getProtocol().equals("jar")) continue;
                String jarPath = resource.getPath().substring(5, resource.getPath().indexOf("!"));

                try (JarFile jarFile = new JarFile(jarPath)) {
                    Enumeration<JarEntry> entries = jarFile.entries();
                    while (entries.hasMoreElements()) {
                        JarEntry entry = entries.nextElement();
                        String entryName = entry.getName();
                        if (!entryName.startsWith(packagePath) || !entryName.endsWith(".class")) continue;
                        String className = entryName.substring(0, entryName.length() - 6).replace('/', '.');

                        try {
                            classes.add(Class.forName(className));
                        } catch (ClassNotFoundException e) {
                            throw new ClassNotFoundException("Failed to load class: " + className, e);
                        }
                    }
                } catch (IOException e) {
                    throw new ClassNotFoundException("Failed to read JAR file: " + jarPath, e);
                }
            }
        } catch (IOException e) {
            throw new ClassNotFoundException("Failed to get resources for package " + packageName, e);
        }

        return classes;
    }
}
